﻿#include "mconnectiongroup.h"

#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>

#include <QTimer>
#define MAX_NUM 2


class MConnectionGroupPrivate
{
public:
    MConnectionGroupPrivate() {
        m_ptr = 0;
    }
public:
    QMap<uint,QTcpSocket*> clientMap;
    QJsonArray roles;
    QJsonObject *roleStaObj;
    QTimer *refTimer;
    int index;
    qintptr m_ptr;
};

MConnectionGroup::MConnectionGroup(qintptr ptr, QObject *parent)
    : QObject(parent)
{
    d = new MConnectionGroupPrivate;
    d->roleStaObj = new QJsonObject();
    d->m_ptr = ptr;
    
    d->refTimer = new QTimer(this);
    d->refTimer->setInterval(30);
    connect(d->refTimer,SIGNAL(timeout()),this,SLOT(sendRefreshDatas()));
}

MConnectionGroup::~MConnectionGroup()
{
    delete d->roleStaObj;
    delete d;
}

void MConnectionGroup::startInit()
{ 
    if( d->m_ptr > 0 )
        addNewConnect(d->m_ptr);
}


/*!@有新连接时
*/
void MConnectionGroup::addNewConnect(qintptr sktDescriptor)
{
    if( d->clientMap.count() >= MAX_NUM ){
        emit connectionFull(sktDescriptor);
        return;
    }
    
    //用sktDescriptor构建socket对象
    QTcpSocket *newSkt =new QTcpSocket(this);
    newSkt->setSocketDescriptor(sktDescriptor);
    
    int newId = sktDescriptor;
    d->clientMap.insert(newId,newSkt);
    
    //newLog("server","new onnection,"+newSkt->localAddress().toString()+" id:"+QString::number(newId));
    
    //建立链接关系
    connect(newSkt, SIGNAL(readyRead()), this, SLOT(clientReadyRead()));
    connect(newSkt, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
    
    //收到新客户端链接，发送给客户端id用于客户端构建对象
    QJsonObject jsObj;
    jsObj.insert("cmd","init");
    jsObj.insert("id",newId);
    newSkt->write(QJsonDocument(jsObj).toJson());
    
    if( !d->refTimer->isActive() && d->clientMap.count() > 1 && !d->refTimer->isActive() )
        d->refTimer->start();
    
    emit onlineChange(true);
}


void MConnectionGroup::clientReadyRead()
{
    QTcpSocket *socket = (QTcpSocket*)sender();
    if( socket )
    {
        QByteArray data = socket->readAll();
        QJsonObject jsObj = getRootJsObj(data);
        QString cmd = jsObj.value("cmd").toString();
        if( cmd == "role_refresh" )
        {//客户端刷新
            d->roleStaObj->insert(jsObj.value("id").toVariant().toString(),jsObj);
        }
        else if( cmd == "role_register" )
        {//客户端构建出角色对象后注册
            //保存新成员
            jsObj.remove("cmd");
            d->roles.append(jsObj);
            
            //检查同步所有成员到每个客户端
            QTimer::singleShot(100,this,SLOT(sendMembers()));
            
        }
        else if( cmd == "role_syncConfirm" )
        {//发送角色同步数据
            roleSyncConfrimProcess(jsObj.value("id").toInt(), jsObj.value("data").toString().split(","));
        }
        //newLog(QString("user: %1").arg(QString::number(d->clients.indexOf(socket)))+"-"+QString::number(socket->localPort()),socket->readAll());
    }
}

/*!@ 当客户端断开连接时
 * des: 
*/
void MConnectionGroup::socketDisconnected()
{
    QTcpSocket *socket= qobject_cast<QTcpSocket *>(sender());
    int sId = d->clientMap.key(socket);
    d->clientMap.remove(sId);
    
    //准备数据
    QJsonObject jsObj;
    jsObj.insert("cmd","removeMember");
    jsObj.insert("id",sId);
    
    //发送到所有客户端
    foreach (QTcpSocket *skt, d->clientMap)
        skt->write(QJsonDocument(jsObj).toJson());
    
    //删掉d->roles中断开链接的对象
    QJsonObject cObj;
    for(int i=0;i<d->roles.count();i++){
        cObj = d->roles.at(i).toObject();
        if( cObj.value("id").toInt() == sId ){
            d->roles.removeAt(i);
            break;
        }
    }
    
    //
    if( !d->roleStaObj->value(QString::number(sId)).isUndefined() )
        d->roleStaObj->remove(QString::number(sId));
    
    emit onlineChange(false);
    if( d->clientMap.count() <= 0 ){
        emit releaseSpace();
    }
    
}

/*!@ 发送成员到每个客户端
 * des: 每当有新的连接加入并注册成功时，就会给所有客户端分发一次所有的客户端对象信息
*/
void MConnectionGroup::sendMembers()
{
    if( d->roles.count() <=1 )
        return;
    
    //准备数据
    QJsonObject jsObj;
    jsObj.insert("cmd","addMember");
    jsObj.insert("data",d->roles);
    
    foreach (QTcpSocket *skt, d->clientMap){
        skt->write(QJsonDocument(jsObj).toJson());
    }
}

/*!@ 收到角色同步确认，做出相应处理
 * des: 当客户端收到成员对象信息并作出处理后，发出确认信息到服务端，服务端检查与是否完成同步，若不同步，就继续发送没有同步的成员对象信息
*/
void MConnectionGroup::roleSyncConfrimProcess(int id, const QStringList &ids)
{
    QList<int> noIds;
    
    if( d->roles.count() > 0 )
    {
        QJsonObject jsObj;
        foreach (QJsonValue jsV, d->roles) {
            jsObj = jsV.toObject();
            if( ids.indexOf(jsObj.value("id").toVariant().toString() ) < 0 )
                noIds.append(jsObj.value("id").toInt());
        }
    }
    else if( ids.count()<=0 || ids.indexOf(QString::number(id)) < 0 ){
        noIds.append(id);
    }
    
    if( noIds.count() > 0)
    {
        QTcpSocket *skt = d->clientMap.value(id);
        if( !skt )
            return;
        
        if( noIds.indexOf(id) >=0 )
        {//需要发送 init命令 ----应该不会出现
            QJsonObject jsObj;
            jsObj.insert("cmd","init");
            jsObj.insert("id",id);
            skt->write(QJsonDocument(jsObj).toJson());
            noIds.removeAt(noIds.indexOf(id));
        }
        
        if( noIds.length() > 0 )
        {
            QJsonObject jsObj;
            QJsonArray jsArry;
            foreach (QJsonValue jsV, d->roles) {
                jsObj = jsV.toObject();
                if( noIds.indexOf(jsObj.value("id").toInt()) >= 0  )
                    jsArry.append(jsObj);
            }
            if( jsArry.count() >0  ){
                QJsonObject jsObj;
                jsObj.insert("cmd","addMember");
                jsObj.insert("data",jsArry);
                skt->write(QJsonDocument(jsObj).toJson());
            }
        }
    }
}

/*!@ 发送刷新数据到每个客户端
 * des: 间隔发送所有成员的状态到所有客户端
*/
void MConnectionGroup::sendRefreshDatas(){
    //准备数据
    QStringList keys = d->roleStaObj->keys();
    if( keys.length() <= 1 )
        return;
    
    QJsonObject jsObj;
    jsObj.insert("cmd","refresh");
    QJsonArray jsArry;
    for(int j=0; j<keys.length();j++)
        jsArry.append(d->roleStaObj->value(keys.at(j)));
    jsObj.insert("data",jsArry);
    
    foreach (QTcpSocket *skt, d->clientMap)
        skt->write(QJsonDocument(jsObj).toJson());
}

QJsonObject MConnectionGroup::getRootJsObj(const QByteArray &data)
{
    QJsonParseError jsError;
    QJsonDocument jsDoc = QJsonDocument::fromJson(data, &jsError);
    
    if(jsError.error != QJsonParseError::NoError )
        return QJsonObject();
    
    if(jsDoc.isNull() || jsDoc.isEmpty())
        return QJsonObject();
    
    if( jsDoc.isObject() )
        return jsDoc.object();
    return QJsonObject();
} 
